using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ClassifierLib.Classifier.Filters
{
    /// <summary>
    /// (selection filter equivelent to exploit strategy)
    /// </summary>
    public class ExploitSelect : SelectFilter
    {
        private double _totalPrediction;
        private double _totalFitness;
        private double _totalPopulation;
        //private Dictionary<Action, double> _sizeOutput = new Dictionary<Action, double>();    //TODO: rename
        //private Dictionary<Action, double> _predN = new Dictionary<Action, double>();         //TODO: rename
        //private Dictionary<Action, double> _predSig = new Dictionary<Action, double>();         //TODO: rename
        private List<PredictionQuality> _predictionQualityArray = new List<PredictionQuality>();

        public ExploitSelect()
        {
            this.Name = "Exploit Select";
            if (!RegisteredTypes.Contains(GetType()))
                RegisteredTypes.Add(GetType());   
        }

        public override void CalculateStatistics(Population Population, Population MatchSet)
        {

            ClearStatisticsVariables(MatchSet.Count);

            foreach (Rule r in MatchSet)
            {
                bool actionExists = false;
                double rPQ = r.Statistics["prediction"] * r.Statistics["fitness"] * r.Numerosity;     //Could multiply by numerosity

                //check if r's action already exists in _predictionQualityArray
                foreach (PredictionQuality pq in _predictionQualityArray)
                {
                    if (r.Action.Equals(pq.Action))   //action already in array
                    {
                        if (rPQ > pq.QualityOfPrediction)
                            pq.QualityOfPrediction = rPQ;

                        actionExists = true;
                    }
                }
                if (!actionExists)
                {
                    //create new instance of PredictionQuality and add new action, then add to our array
                    PredictionQuality newPQ = new PredictionQuality();
                    newPQ.Action = r.Action;
                    newPQ.QualityOfPrediction = rPQ;
                    _predictionQualityArray.Add(newPQ);

                }
            }

            //Code below calculates average prediction quality over each action within matchset, rather than 
            //individuals. This is ZCS format.
            /*
            foreach (Rule r in MatchSet)
            {
                bool actionExists = false;

                //check if r's action already exists in _predictionQualityArray
                foreach (PredictionQuality pq in _predictionQualityArray)
                {
                    if (r.Action.Equals(pq.Action))   //action already in array
                    {
                        pq.QualityOfPrediction += r.Statistics["prediction"] * r.Statistics["fitness"];     //Could multiply by numerosity
                        actionExists = true;
                    }
                }
                if (!actionExists)
                {
                    //create new instance of PredictionQuality and add new action, then add to our array
                    PredictionQuality newPQ = new PredictionQuality();
                    newPQ.Action = r.Action;
                    newPQ.QualityOfPrediction = r.Statistics["prediction"] * r.Statistics["fitness"];     //Could multiply by numerosity
                    _predictionQualityArray.Add(newPQ);

                }
            }*/


            //ClearStatisticsVariables(MatchSet.Count);
            //foreach (Rule r in MatchSet)
            //{
            //    double predSigAdditional = r.Statistics["prediction"] * r.Statistics["fitness"]*r.Numerosity;
            //    if (PredSig.ContainsKey(r.Action))
            //    {
            //        PredSig[r.Action] += predSigAdditional;
            //    }
            //    else
            //    {
            //        PredSig[r.Action] = predSigAdditional;
            //    }

            //    double predNAdditional = r.Statistics["fitness"] * r.Numerosity;

            //    if (PredN.ContainsKey(r.Action))
            //    {
            //        PredN[r.Action] += predNAdditional;
            //    }
            //    else
            //    {
            //        PredN[r.Action] = predNAdditional;
            //    }
            //}


            //foreach (Rule r in Population)
            //{
            //    _totalPopulation += r.Numerosity;
            //    _totalFitness += r.Statistics["fitness"];

            //    if (_sizeOutput.ContainsKey(r.Action))
            //        _sizeOutput[r.Action]++;
            //    else
            //        _sizeOutput[r.Action] = 1;

            //    //prevent large neg number affecting the prediction
            //    if (r.Statistics["prediction"] > 0)
            //        _totalPrediction += Math.Abs(r.Statistics["prediction"]);
            //}
        }

        /// <summary>
        /// Resets all dictionaries and constants used during statistics calculations
        /// </summary>
        /// <param name="p"></param>
        private void ClearStatisticsVariables(int p)
        {
            _predictionQualityArray.Clear();

            //PredN.Clear();
            //PredSig.Clear();
            //_sizeOutput.Clear();

            //_totalFitness = _totalPopulation = _totalPrediction = 0;
        }

        public override void SelectionMethod(Population MatchSet)
        {
            GreedySelection(MatchSet);
        }

        private void GreedySelection(Population MatchSet)
        {
            //double highest = -1000000;
            //DesiredAction = null;

            //// calculate _colVal vector
            //foreach (Action key in PredSig.Keys)
            //{
            //    double value = PredSig[key] ;//   / PredN[key];
            //    if (value > highest)
            //    {
            //        highest = value;
            //        DesiredAction = key;
            //    }
            //}

            double highest = -1000000;
            DesiredAction = null;
            foreach (PredictionQuality pq in _predictionQualityArray)
            {
                if (pq.QualityOfPrediction > highest)
                {
                    highest = pq.QualityOfPrediction;
                    DesiredAction = pq.Action;
                }
            }
        }

        //protected Dictionary<Action, double> PredN
        //{
        //    get
        //    {
        //        return _predN;
        //    }
        //}
        ////TODO: needs better name
        //protected Dictionary<Action, double> PredSig
        //{
        //    get
        //    {
        //        return _predSig;
        //    }
        //}

        
    }

    public class PredictionQuality
    {
        public Action Action;
        public double QualityOfPrediction;
    }
}
